package com.shareyi.wox.core;



import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.shareyi.wox.config.ActionBean;
import com.shareyi.wox.config.InterceptorBean;
import com.shareyi.wox.config.InterceptorRef;
import com.shareyi.wox.config.ResultTypeBean;
import com.shareyi.wox.config.WoxConfigException;
import com.shareyi.wox.core.interceptor.Interceptor;
import com.shareyi.wox.result.WoxResultSupport;
import com.shareyi.wox.util.ClassLoaderUtil;
import com.shareyi.wox.util.FillFieldUtil;




public class ObjectFactory  {
	
	
	private static final Log LOG = LogFactory.getLog(ObjectFactory.class);

    private transient ClassLoader ccl;

    public void setClassLoader(ClassLoader cl) {
        this.ccl = cl;
    }
    


    public ObjectFactory() {
    }




    /**
     * Allows for ObjectFactory implementations that support
     * Actions without no-arg constructors.
     *
     * @return true if no-arg constructor is required, false otherwise
     */
    public boolean isNoArgConstructorRequired() {
        return true;
    }

    /**
     * Utility method to obtain the class matched to className. Caches look ups so that subsequent
     * lookups will be faster.
     *
     * @param className The fully qualified name of the class to return
     * @return The class itself
     * @throws ClassNotFoundException
     */
    public Class getClassInstance(String className) throws ClassNotFoundException {
        if (ccl != null) {
            return ccl.loadClass(className);
        }

        return ClassLoaderUtil.loadClass(className, this.getClass());
    }

    /**
     * Build an instance of the action class to handle a particular request (eg. web request)
     * @param actionName the name the action configuration is set up with in the configuration
     * @param namespace the namespace the action is configured in
     * @return instance of the action class to handle a web request
     * @throws Exception
     */
    public Object buildAction(String actionName, String namespace, ActionBean actionBean, Map<String, Object> extraContext) throws Exception {
        return buildBean(actionBean.getClassName(), extraContext);
    }

    /**
     * Build a generic Java object of the given type.
     *
     * @param clazz the type of Object to build
     */
    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
        return clazz.newInstance();
    }


    /**
     * Build a generic Java object of the given type.
     *
     * @param className the type of Object to build
     */
    public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
        Class clazz = getClassInstance(className);
        Object obj = buildBean(clazz, extraContext); 
        return obj;
    }

    /**
     * Builds an Interceptor from the InterceptorConfig and the Map of
     * parameters from the interceptor reference. Implementations of this method
     * should ensure that the Interceptor is parameterized with both the
     * parameters from the Interceptor config and the interceptor ref Map (the
     * interceptor ref params take precedence), and that the Interceptor.init()
     * method is called on the Interceptor instance before it is returned.
     *
     * @param interceptorRef    the InterceptorConfig from the configuration
     */
    public Interceptor buildInterceptor(InterceptorRef interceptorRef) throws WoxConfigException {
    	InterceptorBean interceptorBean=interceptorRef.getInterceptorBean();
        String interceptorClassName = interceptorBean.getClassName();
        Map<String, String> thisInterceptorClassParams = interceptorRef.getParamMap();
        Map<String, String> params = (thisInterceptorClassParams == null) ? new HashMap<String, String>() : new HashMap<String, String>(thisInterceptorClassParams);
        params.putAll(interceptorBean.getParams());

        String message;
        Throwable cause;

        try {
            // interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra context
            Interceptor interceptor = (Interceptor) buildBean(interceptorClassName, null);
            interceptor.init(interceptorRef.getConfigParams());
            return interceptor;
        } catch (InstantiationException e) {
            cause = e;
            message = "Unable to instantiate an instance of Interceptor class [" + interceptorClassName + "].";
        } catch (IllegalAccessException e) {
            cause = e;
            message = "IllegalAccessException while attempting to instantiate an instance of Interceptor class [" + interceptorClassName + "].";
        } catch (ClassCastException e) {
            cause = e;
            message = "Class [" + interceptorClassName + "] does not implement com.opensymphony.xwork2.interceptor.Interceptor";
        } catch (Exception e) {
            cause = e;
            message = "Caught Exception while registering Interceptor class " + interceptorClassName;
        } catch (NoClassDefFoundError e) {
            cause = e;
            message = "Could not load class " + interceptorClassName + ". Perhaps it exists but certain dependencies are not available?";
        }

        throw new WoxConfigException(message, cause);
    }

    /**
     * Build a Result using the type in the ResultConfig and set the parameters in the ResultConfig.
     *
     * @param resultConfig the ResultConfig found for the action with the result code returned
     * @param extraContext a Map of extra context which uses the same keys
      */
    
    public WoxResultSupport buildResult(ResultTypeBean resultConfig, Map<String, Object> extraContext) throws Exception {
        String resultClassName = resultConfig.getClassName();
        WoxResultSupport result = null;

        if (resultClassName != null) {
            result = (WoxResultSupport) buildBean(resultClassName, extraContext);
            Map<String, Object> params = resultConfig.getParams();
            if (params != null) {
            	FillFieldUtil.fill(result, params);
            }
        }

        return result;
    }

    
	public WoxResultSupport buildResult(String className,	Map<String, Object> extraContext) throws Exception {
		return (WoxResultSupport) buildBean(className, extraContext);
	}
}
